package core;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import tools.Format;
import tools.Utils;

public class Torrent {

    private String createdBy;
    private String announce;
    private int creationDate;
    private String comment;
    private String encoding;
    private ArrayList<String> announceList;
    private int pieceLength;
    private byte[] pieces;
    private int prive;
    private String name;	
    private long length;
    private boolean multi;
    private ArrayList<Map<String, Long>> files;
    private byte[] hashInfo;
    private byte[] bitfield;
    private long downloaded;
    private long uploaded;
	private int avancement;
	private int download_since;
	private int upload_since;

    public Torrent() {
        this.createdBy = null;
        this.announce = null;
        this.creationDate = -1;
        this.comment = null;
        this.encoding = null;
        this.announceList = new ArrayList<String>();
        this.pieceLength = -1;
        this.pieces = null;
        this.prive = -1;
        this.name = null;
        this.length = -1;
        this.multi = false;
        this.files = new ArrayList<Map<String, Long>>();
        this.hashInfo = new byte[20];
        this.bitfield = null;
        this.downloaded = 0;
        this.uploaded = 0;
        this.upload_since =0 ;
        this.download_since = 0;
    }

    private void fill(String key, BeValue value) throws InvalidBEncodingException {
        if (key.equals("created by")) {
            this.createdBy = value.getString();
        } else if (key.equals("announce")) {
            this.announce = value.getString();
        } else if (key.equals("encoding")) {
            this.encoding = value.getString();
        } else if (key.equals("announce-list")) {
            List list = value.getList();
            for (int i = 0; i < list.size(); i++) {
                List curList = ((BeValue) list.get(i)).getList();
                for (int j = 0; j < curList.size(); j++) {
                    this.announceList.add(((BeValue) curList.get(j)).getString());
                }
            }
        } else if (key.equals("creation date")) {
            this.creationDate = value.getInt();
        } else if (key.equals("comment")) {
            this.comment = value.getString();
        } else if (key.equals("info")) {
            this.load(value);
        } else if (key.equals("pieces")) {
            this.pieces = value.getBytes();
            this.bitfield = new byte[(this.pieces.length / 8) + 1];
        } else if (key.equals("name")) {
            this.name = value.getString();
        } else if (key.equals("length")) {
            this.length = value.getLong();
        } else if (key.equals("piece length")) {
            this.pieceLength = value.getInt();
        } else if (key.equals("private")) {
            this.prive = value.getInt();
        } else if (key.equals("files")) {
            this.multi = true;
            List list = value.getList();

            for (int i = 0; i < list.size(); i++) {
                Map map = ((BeValue) list.get(i)).getMap();

                Set keys = map.keySet();
                Collection values = map.values();
                Iterator<String> iterKey = keys.iterator();
                Iterator<BeValue> iterValue = values.iterator();
                String keyMap = "";
                BeValue valueMap = null;

                String filePath = "";
                long fileSize = -1;

                while (iterKey.hasNext() && iterValue.hasNext()) {
                    keyMap = iterKey.next();
                    valueMap = iterValue.next();
                    if (keyMap.equals("length")) {
                        fileSize = valueMap.getLong();
                        this.length += fileSize;
                    } else if (keyMap.equals("path")) {
                        List pathList = valueMap.getList();
                        filePath = "";
                        if (pathList.size() > 1) {
                            for (int j = 0; j < pathList.size(); j++) {
                                filePath += "/" + ((BeValue) pathList.get(j)).getString();
                            }
                        } else {
                            filePath = ((BeValue) pathList.get(0)).getString();
                        }

                    }
                }

                Map<String, Long> fileMap = new HashMap<String, Long>();
                fileMap.put(filePath, fileSize);
                this.files.add(fileMap);
            }
        }

    }

    public byte[] getBitfield() {
        return bitfield;
    }

    public void load(BeValue beValue) throws InvalidBEncodingException {
        Map map = beValue.getMap();

        Set keys = map.keySet();
        Collection values = map.values();
        Iterator<String> iterKey = keys.iterator();
        Iterator<BeValue> iterValue = values.iterator();
        String key = "";
        BeValue value = null;

        while (iterKey.hasNext() && iterValue.hasNext()) {
            key = iterKey.next();
            value = iterValue.next();
            this.fill(key, value);
        }

    }

    private String format(String key, Object value) {
        if (value == null) {
            return "";
        }

        if (value instanceof List && ((List) value).size() == 0) {
            return "";
        }

        if (value instanceof Integer && ((Integer) value) == -1) {
            return "";
        }

        return key + " : " + value + "\n";
    }

    @Override
    public String toString() {
        String toString =
                format("Created By", this.createdBy)
                + format("Announce", this.announce)
                + format("Creation Date", this.creationDate)
                + format("Comment", this.comment)
                + format("Encoding", this.encoding)
                + format("Announce-List", this.announceList)
                + format("Piece length", this.pieceLength)
                + /*
                 * format("Pieces length % 20", this.pieces.length % 20) +
                 */ format("Private", this.prive)
                + format("Name", this.name)
                + format("Length", this.length)
                + /*
                 * format("Md5sum", this.md5sum)+
                 */ format("Multiple-File", this.multi)
                + format("Files", this.files);

        return toString;
    }

    public boolean isMulti() {
        return multi;
    }

    public ArrayList<Map<String, Long>> getFiles() {
        return files;
    }

    public String getCreatedBy() {
        return createdBy;
    }

    public String getAnnounce() {
        return announce;
    }

    public int getCreationDate() {
        return creationDate;
    }

    public String getComment() {
        return comment;
    }

    public String getEncoding() {
        return encoding;
    }

    public ArrayList<String> getAnnounceList() {
        return announceList;
    }

    public int getPieceLength() {
        return pieceLength;
    }

    public byte[] getPieces() {
        return pieces;
    }

    public int getPrive() {
        return prive;
    }

    public String getName() {
        return name;
    }

    public long getLength() {
        return length;
    }


    public void setHashInfo(byte[] hashInfo) {

        this.hashInfo = hashInfo;
    }

    public byte[] getHashInfo() {
        return hashInfo;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + Arrays.hashCode(hashInfo);
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        result = prime * result + prive;
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        Torrent other = (Torrent) obj;
        if (!Arrays.equals(hashInfo, other.hashInfo)) {
            return false;
        }
        if (name == null) {
            if (other.name != null) {
                return false;
            }
        } else if (!name.equals(other.name)) {
            return false;
        }
        if (prive != other.prive) {
            return false;
        }
        return true;
    }

    public void increaseDownloaded(int addValue) {
        this.downloaded += addValue; 
        this.download_since += addValue;
        
        this.avancement = (int) ((this.downloaded *100) / this.length);        
    }

    public void increaseUploaded(int length) {
        this.uploaded += length;
        this.upload_since += length;
    }

	public int getAvancement() {
		return avancement;
	}
	
	public int getDownloadSince(){
		
		int valeur = this.download_since;
		this.download_since =0;
		return valeur;
	}
	
	public int getUploadSince(){
		
		int valeur = this.upload_since;
		this.upload_since =0;
		return valeur;
	}
    
    
}
